#pragma once
#include <thread>
#include <condition_variable>

typedef std::function<void ()> FunctionVoid;
typedef std::function<void (void *)> FunctionWithObject;
 
// updates one of two objects, while the other can be accessed for reading
class TwoObjectUpdater
{
	void* mTwoObjects[2];
	int mReferenceCountForReading[2];

	int mIndexOfValidObject;
	int mIndexOfCurrentlyReadObject;

	std::thread* mThread;
	mutable std::mutex mMutex;
	std::condition_variable mCondition;

	FunctionWithObject mUpdateFunctions[2];
	FunctionWithObject mReleaseFunction;
	FunctionVoid mNotifierFunction;

	// this enumeration helps to avoid the destruction of std::function object
	// in the worker thread
	enum FunctionCondition {
		kShouldRun = 0,
		kIsRunning,
		kIsDiscarded
	};
	FunctionCondition mFunctionConditions[2];

	int mUpdateCount, mCancelCount;
	bool mShouldStop;
	bool mIsInvalidated;
	
public:
	TwoObjectUpdater();
	~TwoObjectUpdater();

	bool IsEmpty() const;
	void StartRunning(void* obj1, void* obj2, 
		FunctionWithObject releaseFunction);
	void StopRunning();
	const void* BeginRead();
	void EndRead(const void* releasedObject);

	void UpdateFreeObject(FunctionWithObject functionPerformingUpdate);
	void SetNotifierCallback(FunctionVoid notifier);
	void Invalidate();
	bool IsCancelled() const;
	int GetTotalUpdateCount() const;

private:
	void Run();
};

